﻿using System.Collections.Generic;
using PhiStore.Packages;
using PhiStore.Util;

namespace PhiStore.Security
{
    internal class CredentialManager
    {
        private Dictionary<string, PhiCredentials> credentials = null;

        private static CredentialManager instance = null;
        private static object mutex = new object();

        private CredentialManager()
        {
            credentials = new Dictionary<string, PhiCredentials>();

            LoadCredentials();
        }

        public static CredentialManager Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (mutex)
                    {
                        instance = new CredentialManager();
                    }
                }
                return instance;
            }
        }

        public bool AddCredentials(PhiCredentials cred)
        {
            bool success = true;
            lock (mutex)
            {
                if (!credentials.ContainsKey(cred.Username))
                {
                    credentials.Add(cred.Username, cred);
                    SaveCredentials();
                }
                else
                {
                    success = false;
                }
            }
            return success;
        }

        public void RemoveCredentials(PhiCredentials cred)
        {
            lock (mutex)
            {
                if (credentials.ContainsKey(cred.Username) && credentials[cred.Username].Equals(cred))
                {

                    credentials.Remove(cred.Username);

                    SaveCredentials();
                }
            }
        }

        public bool ChangeCredentials(PhiCredentials prevCred, PhiCredentials newCred)
        {
            bool success = true;
            lock (mutex)
            {
                if (!credentials.ContainsKey(prevCred.Username))
                {
                    success = false;
                }
                else
                {
                    credentials.Remove(prevCred.Username);
                    credentials.Add(newCred.Username, newCred);
                    SaveCredentials();
                }
            }

            return success;
        }

        public bool CredentialsExist(PhiCredentials cred)
        {
            bool verified = false;
            lock (mutex)
            {
                verified = credentials.ContainsKey(cred.Username);
            }
            return verified;
        }

        public bool VerifyCredentials(PhiCredentials cred)
        {
            bool verified = false;
            lock (mutex)
            {
                if (credentials.ContainsKey(cred.Username))
                {
                    PhiCredentials dictCred = credentials[cred.Username];
                    verified = dictCred.Username == cred.Username
                        && Compare.ArrayEquals(dictCred.Password, cred.Password)
                        && (dictCred.IsServerCredential || dictCred.IsClientCredentials && cred.IsClientCredentials);
                }

            }
            return verified;
        }

        public PhiCredentials GetCredentials(string username)
        {
            PhiCredentials result = null;
            lock (mutex)
            {
                if (credentials.ContainsKey(username))
                {
                    result = credentials[username];
                }
            }
            return result;
        }

        public IEnumerable<PhiCredentials> GetAllCredentials()
        {
            return credentials.Values;
        }

        public ReturnCode performAction(PhiPackage action)
        {
            ReturnCode result = ReturnCode.Failure;
            if (action is Connect)
            {
                PhiCredentials credentials = (action as Connect).Credentials;
                if (VerifyCredentials(credentials))
                {
                    result = ReturnCode.Success;
                }
                else
                {
                    if (CredentialsExist(credentials))
                    {
                        result = ReturnCode.WrongPassword;
                    }
                    else
                    {
                        result = ReturnCode.UnknownUser;
                    }
                }
            }

            return result;
        }
        
        public PhiCredentials GetServerCredential()
        {
            PhiCredentials result = null;
            foreach (PhiCredentials cred in GetAllCredentials())
            {
                if (cred is ServerCredentialContainer)
                {
                    result = cred;
                    break;
                }
            }
            return result;
        }

        #region Settings stuff

        private void LoadCredentials()
        {
            try
            {
                credentials = SettingsManager.Instance.Read<Dictionary<string, PhiCredentials>>("Credentials");
            }
            catch
            {

            }
        }

        private void SaveCredentials()
        {
            SettingsManager.Instance.Add("Credentials", credentials);
        }
        #endregion
    }
}
